import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import threading
import time
import requests
import hashlib
import random
from datetime import datetime
import json
import hmac
import logging
from datetime import timedelta

# 样式管理器（与第一个代码一致）
class StyleManager:
    COLORS = {
        'primary': '#3498db',
        'secondary': '#2ecc71',
        'background': '#f8f9fa',
        'surface': '#ffffff',
        'text': '#2c3e50',
        'error': '#e74c3c',
        'warning': '#f1c40f',
        'success': '#27ae60'
    }

    @classmethod
    def configure_styles(cls):
        style = ttk.Style()
        style.theme_use('clam')
        style.configure('.', 
                      background=cls.COLORS['background'],
                      foreground=cls.COLORS['text'],
                      font=('Segoe UI', 10))
        
        # 框架样式
        style.configure('TLabelframe', 
                       background=cls.COLORS['surface'],
                       relief='flat',
                       bordercolor='#dfe6e9')
        style.configure('TLabelframe.Label', 
                       background=cls.COLORS['surface'],
                       foreground=cls.COLORS['primary'],
                       font=('Segoe UI', 10, 'bold'))
        
        # 按钮样式
        style.configure('TButton',
                       background=cls.COLORS['primary'],
                       foreground=cls.COLORS['surface'],
                       borderwidth=0,
                       padding=8,
                       font=('Segoe UI', 10, 'bold'))
        style.map('TButton',
                 background=[('active', cls.COLORS['secondary']),
                            ('disabled', '#bdc3c7')])
        
        # 输入框样式
        style.configure('TEntry',
                      fieldbackground=cls.COLORS['surface'],
                      bordercolor='#b2bec3',
                      padding=5)
        style.map('TEntry',
                 bordercolor=[('focus', cls.COLORS['primary'])])
        
        # 滚动条样式
        style.configure('Vertical.TScrollbar',
                      background='#dfe6e9',
                      troughcolor=cls.COLORS['background'])
        style.map('Vertical.TScrollbar',
                background=[('active', '#b2bec3')])

# 日志配置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('simulator.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger('MusicSimulator')

class AuthClient:
    def __init__(self, base_url):
        self.base_url = base_url.rstrip('/')
        self.access_token = None
        self.refresh_token = None
        self.token_expire = None
        self.session = requests.Session()
        self.session.verify = True  # 仅测试环境禁用SSL验证
        self.session.headers.update({'Content-Type': 'application/json'})

    def get_ticket(self, device_id, secret, retries=3):
        for attempt in range(retries):
            try:
                # 修改为毫秒级时间戳
                timestamp = int(time.time() * 1000)
                response = self.session.post(
                    f"{self.base_url}/api/v1/ticket",
                    json={
                        "device_id": device_id,
                        "timestamp": timestamp
                    },
                    timeout=5
                )
                
                if 200 <= response.status_code < 300:
                    data = response.json()
                    if data.get('code') == 0:
                        return data
                    raise ValueError(f"Ticket error: {data.get('message')}")
                response.raise_for_status()

            except (requests.ConnectionError, requests.Timeout) as e:
                if attempt == retries - 1:
                    raise
                time.sleep(2 ** attempt)
            except Exception as e:
                logger.error(f"Get ticket failed: {str(e)}")
                raise
        return None

    def get_token(self, ticket, device_id, secret, retries=3):
        for attempt in range(retries):
            try:
                # 修改为毫秒级时间戳
                timestamp = int(time.time() * 1000)
                # 生成签名
                sign_str = f"{ticket}{device_id}{secret}"
                signature = hashlib.md5(sign_str.encode('utf-8')).hexdigest()
                
                response = self.session.post(
                    f"{self.base_url}/api/v1/token",
                    json={
                        "ticket": ticket,
                        "device_id": device_id,
                        "signature": signature,
                        "timestamp": timestamp
                    },
                    timeout=5
                )

                if 200 <= response.status_code < 300:
                    data = response.json()
                    if data.get('code') != 0:
                        raise ValueError(f"Token error: {data.get('message')}")
                        
                    self.access_token = data['token']
                    self.refresh_token = data['refreshToken']
                    self.token_expire = time.time() + data['expiresIn']
                    return True
                response.raise_for_status()

            except (requests.ConnectionError, requests.Timeout) as e:
                if attempt == retries - 1:
                    raise
                time.sleep(2 ** attempt)
            except Exception as e:
                logger.error(f"Get token failed: {str(e)}")
                raise
        return False

class PlaybackSimulator:
    def __init__(self, device_id, user_id, auth_client, interval):
        self.device_id = device_id
        self.user_id = user_id
        self.auth_client = auth_client
        self.interval = interval
        self.running = False
        self.success_count = 0
        self.fail_count = 0
        self.lock = threading.Lock()
        self.ui_log_callback = None

    def generate_playback_data(self):
        # 生成随机歌曲ID (1-840)
        song_id = random.randint(1, 840)
        
        # 确保user_id是整数类型
        user_id = self.user_id
        if isinstance(user_id, str) and user_id.isdigit():
            user_id = int(user_id)
        
        data = {
            "device_id": self.device_id,
            "user_id": user_id,
            "timestamp": int(time.time() * 1000),
            "song_id": int(song_id)
        }
        
        logger.info("生成播放数据:\n%s", json.dumps(data, indent=2, ensure_ascii=False))
        return data

    def send_data(self, data, retries=10):
        retry_logs = []
        for attempt in range(retries):
            attempt_num = attempt + 1
            try:
                # 记录开始尝试的日志
                start_time = datetime.now()
                retry_logs.append(f"⏱️ 第{attempt_num}次尝试 [{start_time:%H:%M:%S.%f}]")
                
                response = self.auth_client.session.post(
                    f"{self.auth_client.base_url}/api/v1/data",  # 修改为正确的数据接收接口
                    headers={"Authorization": f"Bearer {self.auth_client.access_token}"},
                    json=data,
                    timeout=5
                )
                
                if 200 <= response.status_code < 300:
                    result = response.json()
                    if result.get('code') == 0:
                        with self.lock:
                            self.success_count += 1
                        retry_logs.append(f"✅ 成功: 服务器接受数据 (HTTP {response.status_code})")
                        return True, retry_logs
                    error_msg = result.get('message', '未知错误')
                    retry_logs.append(f"❌ 失败: 服务器拒绝数据 (HTTP {response.status_code}): {error_msg}")
                    logger.error(f"Server rejected data (attempt {attempt_num}/{retries}): {error_msg}")
                else:
                    retry_logs.append(f"❌ 失败: HTTP错误 {response.status_code}")
                    logger.error(f"HTTP error (attempt {attempt_num}/{retries}): {response.status_code}")
                
                # 最后一次尝试失败后才计数
                if attempt == retries - 1:
                    with self.lock:
                        self.fail_count += 1

            except Exception as e:
                error_type = type(e).__name__
                retry_logs.append(f"❌ 失败: {error_type} - {str(e)}")
                logger.error(f"Send data failed (attempt {attempt_num}/{retries}): {str(e)}")
                if attempt == retries - 1:  # 最后一次尝试才计数
                    with self.lock:
                        self.fail_count += 1
                
            # 指数退避重试
            if attempt < retries - 1:
                wait_time = (2 ** attempt) + random.uniform(0, 1)
                retry_logs.append(f"⏳ 等待 {wait_time:.1f} 秒后重试...")
                logger.info(f"等待 {wait_time:.1f} 秒后重试...")
                time.sleep(wait_time)
        
        return False, retry_logs

    def start(self):
        self.running = True
        logger.info("Playback simulator started")
        
        while self.running:
            try:
                data = self.generate_playback_data()
                success, retry_logs = self.send_data(data)
                
                # 基本数据信息
                log_msg = (
                    f"🎵 [{datetime.now():%H:%M:%S}] 播放记录 - 用户: {self.user_id}\n"
                    f"设备: {self.device_id} | 歌曲ID: {data['song_id']}\n"
                )
                
                # 添加发送状态和重传日志
                if success:
                    log_msg += f"📤 状态: 成功\n"
                else:
                    log_msg += f"📤 状态: 失败（已重试{len(retry_logs)}次）\n"
                
                # 添加重传详细日志
                if len(retry_logs) > 1:  # 如果有重传，显示详细日志
                    log_msg += "📋 重传详情:\n"
                    for log in retry_logs:
                        log_msg += f"  {log}\n"
                
                if self.ui_log_callback:
                    self.ui_log_callback(log_msg)
                
                time.sleep(self.interval)
            except Exception as e:
                logger.error(f"Simulator error: {str(e)}")
                if self.ui_log_callback:
                    self.ui_log_callback(f"❗ 模拟器错误: {str(e)}\n")
                break
                
        logger.info("Playback simulator stopped")

    def stop(self):
        self.running = False

class SimulatorUI(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("🎵 云音乐设备模拟器 v5.0")
        self.geometry("1000x800")
        self.configure(bg=StyleManager.COLORS['background'])
        StyleManager.configure_styles()
        
        self.auth_client = None
        self.simulator = None
        self.running = False
        self.is_powered_on = False  # 设备电源状态
        self.device_state_updater = None  # 设备状态更新器
        self.create_widgets()
        self.protocol("WM_DELETE_WINDOW", self.on_close)

    def create_widgets(self):
        main_frame = ttk.Frame(self)
        main_frame.pack(fill='both', expand=True, padx=20, pady=20)

        # 配置面板
        config_frame = ttk.LabelFrame(main_frame, text="⚙️ 设备与用户配置")
        config_frame.pack(fill='x', pady=5)

        # 服务器配置
        server_frame = ttk.LabelFrame(config_frame, text="服务器配置")
        server_frame.pack(fill='x', padx=10, pady=5)
        
        ttk.Label(server_frame, text="服务器地址:").grid(row=0, column=0, padx=5, sticky='w')
        self.server_url = ttk.Entry(server_frame, width=50)
        self.server_url.insert(0, "https://smartsound.top")
        self.server_url.grid(row=0, column=1, padx=5, sticky='ew')

        # 设备配置
        device_frame = ttk.LabelFrame(config_frame, text="设备配置")
        device_frame.pack(fill='x', padx=10, pady=5)
        
        ttk.Label(device_frame, text="设备ID:").grid(row=0, column=0, padx=5, sticky='w')
        self.device_id_entry = ttk.Entry(device_frame, width=25)
        self.device_id_entry.grid(row=0, column=1, padx=5, sticky='w')
        
        ttk.Label(device_frame, text="设备密钥:").grid(row=1, column=0, padx=5, pady=5, sticky='w')
        self.device_secret_entry = ttk.Entry(device_frame, width=25, show="*")
        self.device_secret_entry.grid(row=1, column=1, padx=5, sticky='w')
        
        # 添加显示/隐藏密钥的按钮
        self.show_secret_var = tk.BooleanVar(value=False)
        self.show_secret_check = ttk.Checkbutton(
            device_frame, 
            text="显示密钥",
            variable=self.show_secret_var,
            command=self.toggle_secret_visibility
        )
        self.show_secret_check.grid(row=1, column=2, padx=5, sticky='w')

        # 用户配置
        user_frame = ttk.LabelFrame(config_frame, text="用户配置")
        user_frame.pack(fill='x', padx=10, pady=5)

        ttk.Label(user_frame, text="用户ID:").grid(row=0, column=0, padx=5, pady=5, sticky='w')
        self.user_id_entry = ttk.Entry(user_frame, width=30)
        self.user_id_entry.grid(row=0, column=1, padx=5, sticky='ew')

        ttk.Label(user_frame, text="播放间隔（秒）:").grid(row=1, column=0, padx=5, pady=5, sticky='w')
        self.interval_entry = ttk.Entry(user_frame, width=10)
        self.interval_entry.insert(0, "5")
        self.interval_entry.grid(row=1, column=1, padx=5, sticky='w')

        # 设备属性配置框架
        status_frame = ttk.LabelFrame(config_frame, text="📡 设备状态配置")
        status_frame.pack(fill='x', padx=10, pady=5)

        # 电源状态配置
        power_frame = ttk.Frame(status_frame)
        power_frame.pack(fill='x', pady=2)
        
        ttk.Label(power_frame, text="电源状态:").grid(row=0, column=0, padx=2)
        self.power_state = ttk.Combobox(
            power_frame, 
            values=["ON", "OFF"], 
            width=6, 
            state="readonly"
        )
        self.power_state.current(0)
        self.power_state.grid(row=0, column=1, padx=2)
        # 绑定电源状态变更事件
        self.power_state.bind("<<ComboboxSelected>>", self.on_power_state_change)
        
        ttk.Label(power_frame, text="剩余电量:").grid(row=0, column=2, padx=2)
        self.battery_level = ttk.Spinbox(power_frame, from_=0, to=100, width=5)
        self.battery_level.set(80)
        self.battery_level.grid(row=0, column=3, padx=2)
        # 绑定电量变更事件
        self.battery_level.bind("<FocusOut>", self.on_status_change)
        
        ttk.Label(power_frame, text="充电状态:").grid(row=0, column=4, padx=2)
        self.charging_state = ttk.Combobox(
            power_frame, 
            values=["Charging", "Discharging", "Full"],
            width=12, 
            state="readonly"
        )
        self.charging_state.current(0)
        self.charging_state.grid(row=0, column=5, padx=2)
        # 绑定充电状态变更事件
        self.charging_state.bind("<<ComboboxSelected>>", self.on_status_change)

        # 网络状态配置
        net_frame = ttk.Frame(status_frame)
        net_frame.pack(fill='x', pady=2)
        
        ttk.Label(net_frame, text="WiFi状态:").grid(row=0, column=0, padx=2)
        self.wifi_state = ttk.Combobox(
            net_frame, 
            values=["Connected", "Disconnected"], 
            width=12, 
            state="readonly"
        )
        self.wifi_state.current(0)
        self.wifi_state.grid(row=0, column=1, padx=2)
        # 绑定WiFi状态变更事件
        self.wifi_state.bind("<<ComboboxSelected>>", self.on_status_change)
        
        ttk.Label(net_frame, text="网络延迟:").grid(row=0, column=2, padx=2)
        self.net_delay = ttk.Spinbox(net_frame, from_=0, to=1000, width=5)
        self.net_delay.set(100)
        self.net_delay.grid(row=0, column=3, padx=2)
        # 绑定网络延迟变更事件
        self.net_delay.bind("<FocusOut>", self.on_status_change)

        # 音频配置
        audio_frame = ttk.Frame(status_frame)
        audio_frame.pack(fill='x', pady=2)
        
        ttk.Label(audio_frame, text="音效模式:").grid(row=0, column=0, padx=2)
        self.audio_effect = ttk.Combobox(
            audio_frame, 
            values=["Common", "Rock", "Pop", "Jazz"], 
            width=8, 
            state="readonly"
        )
        self.audio_effect.current(0)
        self.audio_effect.grid(row=0, column=1, padx=2)
        # 绑定音效模式变更事件
        self.audio_effect.bind("<<ComboboxSelected>>", self.on_status_change)
        
        ttk.Label(audio_frame, text="播放模式:").grid(row=0, column=2, padx=2)
        self.play_mode = ttk.Combobox(
            audio_frame, 
            values=["Sequential", "Random", "Repeat"], 
            width=10, 
            state="readonly"
        )
        self.play_mode.current(0)
        self.play_mode.grid(row=0, column=3, padx=2)
        # 绑定播放模式变更事件
        self.play_mode.bind("<<ComboboxSelected>>", self.on_status_change)

        # 控制按钮
        btn_frame = ttk.Frame(main_frame)
        btn_frame.pack(fill='x', pady=10)
        
        self.power_btn = ttk.Button(
            btn_frame, 
            text="⚡ 开机",
            command=self.toggle_power,
            width=15
        )
        self.power_btn.pack(side='left', padx=5)

        self.start_btn = ttk.Button(
            btn_frame, 
            text="▶️ 开始播放",
            command=self.start_simulator,
            state='disabled',
            width=15
        )
        self.start_btn.pack(side='left', padx=5)

        self.stop_btn = ttk.Button(
            btn_frame, 
            text="⏹️ 停止播放", 
            command=self.stop_simulator,
            state='disabled',
            width=15
        )
        self.stop_btn.pack(side='left', padx=5)

        # 日志区域
        log_frame = ttk.LabelFrame(main_frame, text="📋 实时日志")
        log_frame.pack(fill='both', expand=True)
        
        self.log_text = scrolledtext.ScrolledText(
            log_frame, 
            wrap=tk.WORD,
            height=10,
            font=('Consolas', 9),
            bg=StyleManager.COLORS['surface'],
            padx=15,
            pady=15
        )
        self.log_text.pack(fill='both', expand=True)

    def toggle_power(self):
        if not self.is_powered_on:
            # 开机
            self.is_powered_on = True
            self.power_btn.config(text="⚡ 关机")
            self.log("🔌 设备开机中...")
            
            # 自动进行设备认证
            self.do_auth()
            
            # 启动设备状态更新器
            self.start_device_state_updater()
        else:
            # 关机
            self.is_powered_on = False
            self.power_btn.config(text="⚡ 开机")
            
            # 停止播放模拟器
            self.stop_simulator()
            
            # 停止设备状态更新器
            self.stop_device_state_updater()
            
            # 禁用按钮
            self.start_btn.config(state='disabled')
            self.stop_btn.config(state='disabled')
            
            # 更新电源状态
            self.power_state.current(1)  # 设置为OFF
            self.report_status()  # 上报状态
            
            self.log("🔌 设备已关机")

    def on_power_state_change(self, event):
        # 当电源状态下拉框变化时触发
        new_state = self.power_state.get()
        if new_state == "OFF" and self.is_powered_on:
            # 如果选择了关机，则调用关机功能
            self.toggle_power()
        elif new_state == "ON" and not self.is_powered_on:
            # 如果选择了开机，则调用开机功能
            self.toggle_power()
        else:
            # 状态变更，上报状态
            self.report_status()

    def on_status_change(self, event):
        # 当任何状态变更时，自动上报设备状态
        if self.is_powered_on and self.auth_client and self.auth_client.access_token:
            self.report_status()

    def toggle_secret_visibility(self):
        # 切换设备密钥的显示/隐藏状态
        if self.show_secret_var.get():
            self.device_secret_entry.config(show="")
        else:
            self.device_secret_entry.config(show="*")

    def report_status(self):
        if not self.auth_client or not self.auth_client.access_token:
            self.log("⚠️ 无法上报状态：设备未认证")
            return
        
        try:
            # 构造上报数据
            status_data = {
                "device_id": self.device_id_entry.get().strip().upper(),
                "timestamp": int(time.time() * 1000),
                "Power": {
                    "state": self.power_state.get(),
                    "battery_level": int(self.battery_level.get()),
                    "isCharging": self.charging_state.get()
                },
                "Net": {
                    "Wifi_state": self.wifi_state.get(),
                    "net_delay": int(self.net_delay.get())
                },
                "Audio": {
                    "effect": self.audio_effect.get(),
                    "mode": self.play_mode.get()
                }
            }
            
            # 发送请求
            response = self.auth_client.session.post(
                f"{self.auth_client.base_url}/api/v1/update_status",
                headers={"Authorization": f"Bearer {self.auth_client.access_token}"},
                json=status_data,
                timeout=5
            )
            
            if response.status_code == 200:
                result = response.json()
                if result.get('code') == 0:
                    self.log("✅ 设备状态上报成功")
                else:
                    self.log(f"❌ 上报失败：{result.get('message')}")
            else:
                self.log(f"⚠️ 服务器返回错误：{response.status_code}")
                
        except ValueError as e:
            self.show_error(f"数值输入错误：{str(e)}")
        except Exception as e:
            self.show_error(f"状态上报失败：{str(e)}")

    def do_auth(self):
        try:
            device_id = self.device_id_entry.get().strip().upper()
            secret = self.device_secret_entry.get().strip()
            base_url = self.server_url.get().strip()
            
            if not device_id:
                raise ValueError("请输入设备ID")
                
            if not secret:
                raise ValueError("请输入设备密钥")
            
            if not base_url.startswith(('http://', 'https://')):
                raise ValueError("服务器地址必须以http://或https://开头")
                
            self.auth_client = AuthClient(base_url)
            
            # 获取票据
            self.log("正在获取设备票据...")
            ticket_res = self.auth_client.get_ticket(device_id, secret)
            
            if not ticket_res:
                raise ValueError("获取票据失败")
                
            # 获取令牌
            self.log("正在获取访问令牌...")
            if self.auth_client.get_token(ticket_res['ticket'], device_id, secret):
                self.log("✔️ 认证成功")
                self.start_btn.config(state='normal')
                
                # 上报设备状态
                self.report_status()
            else:
                raise ValueError("令牌获取失败")
                
        except ValueError as e:
            self.show_error(str(e))
            self.is_powered_on = False
            self.power_btn.config(text="⚡ 开机")
        except Exception as e:
            self.show_error(str(e))
            self.is_powered_on = False
            self.power_btn.config(text="⚡ 开机")

    def start_simulator(self):
        if self.running:
            self.show_warning("播放器已在运行中")
            return
            
        try:
            user_id = int(self.user_id_entry.get())  # 转换为整型
            if user_id <= 0:
                raise ValueError("用户ID必须为正整数")
            
            try:
                interval = float(self.interval_entry.get())
                if interval <= 0:
                    raise ValueError("间隔时间必须大于0")
            except ValueError:
                raise ValueError("请输入有效的数字间隔时间")
            
            device_id = self.device_id_entry.get().strip().upper()  # 设备ID
            
            self.simulator = PlaybackSimulator(
                device_id=device_id,
                user_id=user_id,
                auth_client=self.auth_client,
                interval=interval
            )
            self.simulator.ui_log_callback = self.log
            
            self.running = True
            self.thread = threading.Thread(target=self.simulator.start, daemon=True)
            self.thread.start()
            
            self.start_btn.config(state='disabled')
            self.stop_btn.config(state='normal')
            self.log("🎵 开始播放音乐")

        except Exception as e:
            self.show_error(f"启动失败: {str(e)}")

    def stop_simulator(self):
        if self.simulator:
            self.simulator.stop()
            self.running = False
            
            if self.is_powered_on:
                self.start_btn.config(state='normal')
            else:
                self.start_btn.config(state='disabled')
                
            self.stop_btn.config(state='disabled')
            
            stats = (f"📊 播放统计 - 成功: {self.simulator.success_count}  "
                    f"失败: {self.simulator.fail_count}")
            self.log(stats)
            self.log("🛑 播放已停止")

    def start_device_state_updater(self):
        """启动设备状态更新器"""
        if self.device_state_updater is None:
            self.device_state_updater = threading.Thread(
                target=self.update_device_state_loop,
                daemon=True
            )
            self.device_state_updater.start()
            self.log("📡 设备状态监控已启动 (1秒/次)")

    def stop_device_state_updater(self):
        """停止设备状态更新器"""
        self.device_state_updater = None
        self.log("📡 设备状态监控已停止")

    def update_device_state_loop(self):
        """设备状态更新循环"""
        while self.is_powered_on and self.device_state_updater is not None:
            try:
                self.fetch_device_state()
                time.sleep(1)  # 每1秒更新一次
            except Exception as e:
                self.log(f"❌ 获取设备状态失败: {str(e)}")
                time.sleep(5)  # 出错后等待5秒再重试

    def fetch_device_state(self):
        """从服务器获取设备状态"""
        if not self.auth_client or not self.auth_client.access_token:
            self.log("⚠️ 无法获取设备状态：设备未认证")
            return
        
        device_id = self.device_id_entry.get().strip().upper()
        try:
            # 发送请求获取设备状态
            response = self.auth_client.session.get(
                f"{self.auth_client.base_url}/api/v1/get/devicestate",
                params={"device_id": device_id},
                headers={"Authorization": f"Bearer {self.auth_client.access_token}"},
                timeout=5
            )
            
            if response.status_code == 200:
                result = response.json()
                if result.get('code') == 0:
                    self.log("✅ 获取设备状态成功")
                    self.update_ui_with_device_state(result)
                else:
                    self.log(f"❌ 获取设备状态失败：{result.get('message')}")
            else:
                self.log(f"⚠️ 服务器返回错误：{response.status_code}")
                
        except Exception as e:
            self.log(f"❌ 获取设备状态失败：{str(e)}")
            raise

    def update_ui_with_device_state(self, state_data):
        """根据获取的设备状态更新UI"""
        try:
            # 更新电源状态
            power_data = state_data.get('Power', {})
            if 'state' in power_data:
                self.power_state.set(power_data['state'])
            if 'battery_level' in power_data:
                self.battery_level.set(power_data['battery_level'])
            if 'isCharging' in power_data:
                self.charging_state.set(power_data['isCharging'])
            
            # 更新网络状态
            net_data = state_data.get('Net', {})
            if 'Wifi_state' in net_data:
                self.wifi_state.set(net_data['Wifi_state'])
            if 'net_delay' in net_data:
                self.net_delay.set(net_data['net_delay'])
            
            # 更新音频状态
            audio_data = state_data.get('Audio', {})
            if 'effect' in audio_data:
                # 检查是否在可选值列表中，如果不在则添加
                effect = audio_data['effect']
                if effect not in self.audio_effect['values']:
                    values = list(self.audio_effect['values'])
                    values.append(effect)
                    self.audio_effect['values'] = values
                self.audio_effect.set(effect)
            
            if 'mode' in audio_data:
                # 检查是否在可选值列表中，如果不在则添加
                mode = audio_data['mode']
                if mode not in self.play_mode['values']:
                    values = list(self.play_mode['values'])
                    values.append(mode)
                    self.play_mode['values'] = values
                self.play_mode.set(mode)
            
            # 更新用户ID（如果存在）
            if 'user_id' in state_data and not self.user_id_entry.get():
                self.user_id_entry.delete(0, tk.END)
                self.user_id_entry.insert(0, str(state_data['user_id']))
            
            self.log(f"🔄 设备状态已更新 (时间戳: {state_data.get('timestamp')})")
            
        except Exception as e:
            self.log(f"❌ 更新UI状态失败: {str(e)}")

    def on_close(self):
        if messagebox.askokcancel("退出", "确定要退出程序吗？"):
            self.stop_simulator()
            self.stop_device_state_updater()
            self.destroy()

    # 日志辅助方法
    def log(self, message):
        self.log_text.config(state='normal')
        self.log_text.insert('end', f"[{datetime.now():%H:%M:%S}] {message}\n")
        self.log_text.config(state='disabled')
        self.log_text.see('end')
        self.update_idletasks()

    def show_error(self, message):
        messagebox.showerror("错误", message)
        self.log(f"❌ 错误: {message}")

    def show_warning(self, message):
        messagebox.showwarning("警告", message)
        self.log(f"⚠️ 警告: {message}")

if __name__ == "__main__":
    app = SimulatorUI()
    app.mainloop()